{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Street Colouring with OSMnx\n",
    "This was inspired by previous work by Erin Davis and Cedric Scherer.\n",
    "\n",
    "* [The beautiful hidden logic of cities](https://erdavis.com/2019/07/27/the-beautiful-hidden-logic-of-cities/)\n",
    "* [#Berlin road network by name/suffix forming colorful networks](https://twitter.com/CedScherer/status/1195420409434382336)\n",
    "* [Erin's code](https://github.com/erdavis1/RoadColors)\n",
    "* [Cedric's code](https://github.com/Z3tt/30DayMapChallenge)\n",
    "\n",
    "Their code is very interesting in that it shows how to manipulate shapefiles in R in order to extract the data and chart. However, R is not my first language (I speak Python slightly better), and I'm a notorious big fan of OSMnx, so I wanted to see if this could be replicated with my favourite set of tools.\n",
    "\n",
    "**TL;DR**: it can, and it only takes a few lines of code.\n",
    "\n",
    "For comparison and reference, Cedric's version is the one I aimed to replicate directly:\n",
    "![Berlin](images/BERLINORIGINAL.png)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import osmnx as ox\n",
    "import pandas as pd\n",
    "import geopandas as gpd\n",
    "import networkx as nx\n",
    "%matplotlib inline\n",
    "ox.config(log_console=True, use_cache=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Colouring\n",
    "All I do here:\n",
    "* define a set of colours in three language-specific dictionaries\n",
    "* use a function to generate an array of colours while enumerating the \"edges\" (roads or parts of roads)\n",
    "\n",
    "Note the caveat on how to query Nominatim (OpenStreetMap search engine) for the location. The result must be a POLYGON or POLYLINE. If a POINT is returned, you'd better check for multiple results and selecting one that isn't."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dict_ENG = {'row' :'#c60000','close':'#ee9999','gate': '#ec0b88','wynd':'#e18a1e','brae':'#ff8888','den': '#dd3333','lane' :'#de8140','place' :'#a64dff','road': '#019868','street' : '#f6cf71','way': '#0000c6','avenue': '#651eac','drive': '#1e8ae1','terrace':'#009999','crescent': '#003d99','circle':'#33adff'}\n",
    "dict_GER = {'straße': '#f6cf71','weg':'#019868','allee':'#ec0b88','damm' :'#651eac','platz' :'#e18a1e','chaussee' :'#9dd292','see' :'#2b7de5','ufer' :'#2b7de5','steg' :'#2b7de5'}\n",
    "dict_FRA = {'rue' :'#019868','place' : '#f6cf71','avenue' : '#ec0b88','boulevard' : '#651eac','passage' : '#e18a1e','pont' : '#9dd292','quai' : '#2b7de5'}\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This function takes the road name and a 3-letter code for the language and it returns the colour\n",
    "def colourcode(road_name, language):\n",
    "    ret_val = \"#c6c6c6\"\n",
    "    if (language=='GER'):\n",
    "        for key in dict_GER: \n",
    "            if road_name.lower().endswith(key):\n",
    "                ret_val =  dict_GER.get(key)\n",
    "    elif (language=='ENG'):\n",
    "        for key in dict_ENG: \n",
    "            if road_name.lower().endswith(key):\n",
    "                ret_val =  dict_ENG.get(key) \n",
    "    elif (language=='FRA'):\n",
    "        for key in dict_FRA: \n",
    "            if road_name.lower().startswith(key): #note difference from ENG / GER\n",
    "                ret_val =  dict_FRA.get(key)\n",
    "    return ret_val"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set place and language; the place is basically a Nominatim query. It must return a POLYGON/POLYLINE, not a POINT, so you might have to play with it a little, or set which_result below accordingly    \n",
    "place='Berlin, Germany'\n",
    "language='GER'\n",
    "\n",
    "# note the which_result parameter, as per comment above\n",
    "G = ox.graph_from_place(place, network_type='all', which_result=2) \n",
    "\n",
    "'''\n",
    "place='Aberdeen, United Kingdom'\n",
    "language='ENG'\n",
    "\n",
    "# note the which_result parameter, as per comment above\n",
    "G = ox.graph_from_place(place, network_type='all', which_result=1) #note Aberdeen needs which_result to be 1\n",
    "'''\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# For the colouring, we take the attributes from each edge found extract the road name, and use the function above to create the colour array\n",
    "edge_attributes = ox.graph_to_gdfs(G, nodes=False)\n",
    "ec = [colourcode(str(row['name']).lower(), language) for index, row in edge_attributes.iterrows()]\n",
    "\n",
    "# We can finally draw the plot\n",
    "fig, ax = ox.plot_graph(G, bgcolor='white', axis_off=True, node_size=0, node_color='w', node_edgecolor='gray', node_zorder=2,\n",
    "                        edge_color=ec, edge_linewidth=0.5, edge_alpha=1, fig_height=20, dpi=300)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Appendix \n",
    "# Are you curious about other feature of the streets? With this piece of code, you can see what other elements you could colour..\n",
    "\n",
    "edge_attributes = ox.graph_to_gdfs(G, nodes=False)\n",
    "edge_attributes.head() "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
